---------------------------------------------------------------------------------
  -- Design Name : ALU
  -- File Name   : ExMemAlu32.vhd
  -- Function    : ALU Unit
  -- Authors     : Mirko Francuski  2006/0225
  --               Milos Mihajlovic 2006/0039
  -- School      : University of Belgrade
  --               School for Electrical Engineering
  --               Department for Computer Engineering and Information Theory
  -- Subject     : VLSI Computer Systems
---------------------------------------------------------------------------------

library ieee;

use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_misc.all;
use ieee.numeric_std.all;
use work.UserPkg.all;

entity ExMemAlu32 is
  port (
    op:       in  opCode;
    in1:      in  word32;
    in2:      in  word32;
    alu_out:  out word32;
    n:        out std_logic := '0';
    z:        out std_logic := '0';
    c:        out std_logic := '0';
    v:        out std_logic := '0'
  );
end ExMemAlu32;

architecture behavioral of ExMemAlu32 is
  signal result : word32;
  signal resultExt : word32Ext; 
  --signal uin2 : integer;

begin
  process (all)
    variable uin2 : integer;
  begin
    
    uin2 := to_integer(unsigned(in2));
    c <= '0';
    v <= '0';
    resultExt <= (others => '0');
    
    case op is
      when OPC_ADD | OPC_ADDI =>
        resultExt <= std_logic_vector(signed('0' & in1) + signed('0' & in2));
        result <= resultExt(result'left downto 0);

        c <= resultExt(resultExt'left);                                                     -- OPC_ADD(I): carry flag

        v <=  (not in1(in1'left) and not in2(in2'left) and     result(result'left)) or      -- OPC_ADD(I): overflow flag
              (    in1(in1'left) and     in2(in2'left) and not result(result'left));

      when OPC_SUB | OPC_SUBI =>
        result <= std_logic_vector(signed(in1) - signed(in2));

        v <=  (    in1(in1'left) and not in2(in2'left) and not result(result'left)) or      -- OPC_SUB(I): overflow flag
              (not in1(in1'left) and     in2(in2'left) and     result(result'left));

      when OPC_AND =>
        result <= in1 and in2;

      when OPC_OR =>
        result <= in1 or in2;

      when OPC_XOR =>
        result <= in1 xor in2;

      when OPC_NOT =>
        result <= not in1;

      when OPC_SHL =>
        result <= std_logic_vector(shift_left  (unsigned(in1), uin2));

      when OPC_SHR =>
        result <= std_logic_vector(shift_right (unsigned(in1), uin2));

      when OPC_SAR =>
        result <= std_logic_vector(shift_right (  signed(in1), uin2));

      when OPC_ROL =>
        result <= std_logic_vector(rotate_left (unsigned(in1), uin2));

      when OPC_ROR =>
        result <= std_logic_vector(rotate_right(unsigned(in1), uin2));

      when OPC_MOV =>
        result <= in1;

      when OPC_MOVI =>
        result <= in2;

      when others =>                                                        -- <=> when OPC_MOV, a sta cemo kad je greska u opcode-u? -- to se detektuje u ID, nema veze sa ALU
        result <= (others => '0');
    end case;

    z <= not or_reduce(result);                                             -- zero flag

    n <= result(result'left);                                               -- negative flag

    alu_out <= result;                                                      -- result
  
  end process;
end architecture behavioral;